﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.DirectoryServices;

using System.Threading;

using groupAnalyzer.model;
using groupAnalyzer.connectivity;
namespace groupAnalyzer.controller
{
    class NodeAnalyzer
    {
        private Factory factory;
        private LdapConnectivity connectivity;
        private volatile Dictionary<string, DirectoryEntry> rawGroups;

        public Dictionary<string, DirectoryEntry> RawGroups
        {
            get { return rawGroups; }
            set { rawGroups = value; }
        }



        private string analyzeMode;

        public string AnalyzeMode
        {
            get { return analyzeMode; }
            set { analyzeMode = value; }
        }

        private volatile Dictionary<string, Node> visited;

        private string getProperty(string objectDn, string propertyName)
        {
            string propertyValue = "";
            Object tmp = this.rawGroups[objectDn].Properties[propertyName];
            if (this.rawGroups[objectDn].Properties[propertyName].Value != null)
            {
                return this.rawGroups[objectDn].Properties[propertyName].ToString();
            }
            return propertyValue;
        }

        private string getProperty(string objectDn, string propertyName, int index)
        {
            string propertyValue = "";
            Object tmp = this.rawGroups[objectDn].Properties[propertyName];
            if (this.rawGroups[objectDn].Properties[propertyName].Value != null)
            {
                return this.rawGroups[objectDn].Properties[propertyName][index].ToString();
            }
            return propertyValue;
        }

        private void analyze(Node father, DirectoryEntry entry)
        {
            List<DirectoryEntry> usersOfGroupRaw = connectivity.getUsersOfGroup(father.FQDN);
            foreach (DirectoryEntry userRaw in usersOfGroupRaw)
            {
                string username = connectivity.getProperty(userRaw, "name", 0);
                string department = connectivity.getProperty(userRaw, "department", 0);
                string email = connectivity.getProperty(userRaw, "mail", 0); ;
                string fqdn = connectivity.getProperty(userRaw, "distinguishedName", 0);

                User userOfGroup = this.factory.constructUser(username, department, email, fqdn);
                father.addUser(userOfGroup);
            }
            // get all children
            PropertyValueCollection membersRaw = entry.Properties[this.analyzeMode];
            // iterate over all children

            foreach (Object o in membersRaw)
            {
                string objectDn = o.ToString();
                if (!this.rawGroups.ContainsKey(objectDn))
                {
                    continue;
                }

                string name = this.getProperty(objectDn, "name", 0);
                string fqdn = objectDn;
                string description = this.getProperty(objectDn, "description", 0);
                string email = this.getProperty(objectDn, "mail", 0);
                string groupScope = "";
                string groupType = "";
                string notes = this.getProperty(objectDn, "info", 0);

                // construct node
                // check if node is already visited
                if (this.visited.ContainsKey(objectDn))
                {
                    Node child = this.visited[objectDn];
                    father.addChild(child, NodeConnectionMode.cyclic);
                }
                else
                {
                    Node child = this.factory.constructNode(name, fqdn, description, email, groupScope, groupType, notes);
                    father.addChild(child);
                    // put it into visited
                    this.visited[objectDn] = child;

                    // call analyze
                    this.analyze(child, this.rawGroups[o.ToString()]);
                }
                this.rawGroups.Remove(objectDn);
            }
        }

        public void analyze(string objectDn)
        {

            //string objectDn = this.objectDn;
            // abbruchbedingungen
            if (this.rawGroups.Count <= 0 || !this.rawGroups.ContainsKey(objectDn))
            {
                return;
            }
            DirectoryEntry rootEntry = this.rawGroups[objectDn];

            // generate root entry
            string name = this.getProperty(objectDn, "name", 0);
            string fqdn = objectDn;
            string description = this.getProperty(objectDn, "description", 0);
            string email = this.getProperty(objectDn, "mail", 0);
            string groupScope = "";
            string groupType = "";
            string notes = this.getProperty(objectDn, "info", 0);

            Node root = this.factory.constructRootNode(name, fqdn, description, email, groupScope, groupType, notes);
            this.visited[objectDn] = root;
            this.analyze(root, rootEntry);

            // streiche eintrag nach analyze
            this.rawGroups.Remove(objectDn);
        }

        public void initialize(Factory factory, string analyzeMode, LdapConnectivity connectivity, Dictionary<string, DirectoryEntry> rawGroups)
        {

            this.analyzeMode = analyzeMode;
            this.factory = factory;
            this.visited = new Dictionary<string, Node>();
            this.connectivity = connectivity;
            this.rawGroups = rawGroups;
        }

        public NodeAnalyzer(Factory factory, LdapConnectivity connectivity)
        {
            this.initialize(factory, null, connectivity, null);
        }

        public NodeAnalyzer(Factory factory, Dictionary<string, DirectoryEntry> rawGroups, LdapConnectivity connectivity)
        {
            this.initialize(factory, null, connectivity, rawGroups);
        }

        public NodeAnalyzer(Factory factory, string analyzeMode, Dictionary<string, DirectoryEntry> rawGroups, LdapConnectivity connectivity)
        {
            this.initialize(factory, analyzeMode, connectivity, rawGroups);
        }

        public NodeAnalyzer(Factory factory, string analyzeMode, LdapConnectivity connectivity)
        {
            this.initialize(factory, analyzeMode, connectivity, null);
        }
    }
}
